/**
 * 
 * <p>Description:读取配置信息</p>
 * 
 * <p>Author: wang xing yue</p>
 * <p>Date  :Nov 8, 2013</p>
 *
 */

package com.nihao001.parse.config;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.helper.DataUtil;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.nihao001.parse.DefaultConstantMap;
import com.nihao001.parse.Method;
import com.nihao001.parse.utils.Utils;

public class Configuration {

	private File configFile = null;
	private Map<String, ParseConfig> parseConfigMap = Collections
			.synchronizedMap(new HashMap<String, ParseConfig>());

	private Map<String, HeaderConfig> headerConfigMap = Collections
			.synchronizedMap(new HashMap<String, HeaderConfig>());

	public Configuration(File configFile) {
		if (configFile == null || !configFile.exists()) {
			throw new IllegalArgumentException(
					"config_file is null or config_file not exist");
		}
		this.configFile = configFile;

		try {
			this.load(this.configFile);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 读取配置文件
	 * 
	 * @param file
	 * @throws IOException
	 */
	private void load(File file) throws IOException {
		Document doc = Jsoup.parse(file, "UTF-8", "");

		// 解析parse标签
		Elements parseEles = doc.select("parseConfig parse");

		if (parseEles == null || parseEles.size() == 0) {
			throw new IllegalArgumentException(
					"the count of parse element is 0.");
		}

		for (Element ele : parseEles) {
			ParseConfig parseConfig = parseParseDefinition(ele);
			if (parseConfig != null) {
				if (parseConfigMap.containsKey(parseConfig.getId())) {
					throw new IllegalArgumentException("the parse["
							+ parseConfig.getId() + "] element is duplicate.");
				}
				parseConfigMap.put(parseConfig.getId(), parseConfig);
			}
		}

		// 解析headers标签
		Elements headersEles = doc.select("parseConfig headers");
		if (headersEles != null && headersEles.size() > 0) {
			for (Element ele : headersEles) {
				HeaderConfig headerConfig = parseHeadersDefinition(ele);
				if (headerConfigMap.containsKey(headerConfig.getId())) {
					throw new IllegalArgumentException("the headers["
							+ headerConfig.getId() + "] element is duplicate.");
				}
				headerConfigMap.put(headerConfig.getId(), headerConfig);
			}
		}

	}

	private HeaderConfig parseHeadersDefinition(Element headersEle) {
		HeaderConfig headerConfig = new HeaderConfig();

		String id = headersEle.attr("id");
		if (StringUtils.isEmpty(id)) {
			throw new IllegalArgumentException(
					" the attribute[id] of headers element can not be empty");
		}

		headerConfig.setId(id);

		Elements headerEles = headersEle.select("header");
		if (headerEles == null && headerEles.size() == 0) {
			throw new IllegalArgumentException(
					"the size of element header list is 0.");
		}

		if (headerEles != null && headerEles.size() > 0) {
			for (Element ele : headerEles) {
				String name = ele.attr("name");
				String value = ele.attr("value");
				if (StringUtils.isEmpty(name)) {
					throw new IllegalArgumentException(
							" the attribute[name] of header element can not be empty");
				}
				if (StringUtils.isEmpty(value)) {
					throw new IllegalArgumentException(
							" the attribute[value] of header element can not be empty");
				}

				headerConfig.getHeaders().put(name, value);
			}
		}
		return headerConfig;
	}

	/**
	 * 解析parse标签
	 * 
	 * @param ele
	 * @return
	 */
	private ParseConfig parseParseDefinition(Element ele) {
		ParseConfig parseConfig = new ParseConfig();

		String id = ele.attr("id");
		String method = ele.attr("method");
		String url = ele.attr("url");
		String responseEncode = ele.attr("responseEncode");
		String requestEncode = ele.attr("requestEncode");

		String proxyHost = ele.attr("proxyHost");
		String proxyPort = ele.attr("proxyPort");

		if (StringUtils.isEmpty(id.trim())) {
			throw new IllegalArgumentException(
					" the attribute[id] of parse element can not be empty");
		}
		if (StringUtils.isEmpty(url.trim())) {
			throw new IllegalArgumentException(
					" the attribute[url] of parse element can not be empty");
		}
		if (StringUtils.isEmpty(method.trim())) {
			method = "get";
		}
		if (StringUtils.isEmpty(proxyHost)) {
			proxyHost = "";
		}
		if (StringUtils.isEmpty(proxyPort)) {
			proxyPort = "-1";
		}
		if (StringUtils.isEmpty(requestEncode)) {
			requestEncode = DataUtil.defaultCharset;
		}

		parseConfig.setId(id);
		parseConfig.setMethod(Method.valueOf(method.toUpperCase()));
		parseConfig.setUrl(url);
		parseConfig.setResponseEncode(responseEncode);
		parseConfig.setProxyHost(proxyHost);
		parseConfig.setRequestEncode(requestEncode);
		try {
			parseConfig.setProxyPort(Integer.parseInt(proxyPort));
		} catch (Exception e) {
			throw new IllegalArgumentException(
					" the attribute[proxyPort] of parse element can be number.");
		}

		ParameterConfig parameterConfig = parseParameterDefinition(ele);

		ResultConfig resultConfig = null;
		resultConfig = parseResultDefinition(ele);
		parseConfig.setParameterConfig(parameterConfig);
		parseConfig.setResultConfig(resultConfig);

		return parseConfig;
	}

	/**
	 * 解析Result标签
	 * 
	 * @param ele
	 * @return
	 */
	private ResultConfig parseResultDefinition(Element ele) {
		ResultConfig resultConfig = new ResultConfig();

		Elements eles = ele.select("results");

		if (eles == null || eles.size() != 1) {
			throw new IllegalArgumentException(
					" the count of Results element must be 1.");
		}

		Element resultsEle = eles.first();
		String clazz = resultsEle.attr("class");
		String parentSelector = resultsEle.attr("selector");
		String childClazz = resultsEle.attr("childClass");

		if (StringUtils.isEmpty(clazz.trim())) {
			throw new IllegalArgumentException(
					" the attribute[class] of Results element can not be empty");
		}
		if (StringUtils.isEmpty(parentSelector.trim())) {
			throw new IllegalArgumentException(
					" the attribute[selector] of Results element can not be empty");
		}

		String fullClassName = Utils.getFullClassName(clazz);

		// 如果class是list,那么一定要设置childClass，否则抛出错误
		if (fullClassName.equals(DefaultConstantMap.LIST_FULL_NAME)
				&& StringUtils.isEmpty(childClazz.trim())) {
			throw new IllegalArgumentException(
					"You must define childClass, where class is list");
		}
		resultConfig.setClassFullName(fullClassName);
		resultConfig.setSelector(parentSelector);
		if (!StringUtils.isEmpty(childClazz)) {
			resultConfig.setChildClass(Utils.getFullClassName(childClazz));
		}

		Elements resultEles = resultsEle.select("result");
		if (resultEles == null || resultEles.size() == 0) {
			throw new IllegalArgumentException(
					" There is no Parameters element exist.");
		}
		for (Element e : resultEles) {
			Result r = new Result();
			String clazz1 = e.attr("class");
			String propertyName = e.attr("propertyName");
			String selector = e.attr("selector");
			String handler = e.attr("handler");
			String absolute = e.attr("absolute");

			if (StringUtils.isEmpty(clazz1.trim())) {
				throw new IllegalArgumentException(
						" the attribute[class] of Result element can not be empty");
			}
			if (StringUtils.isEmpty(propertyName.trim())) {
				throw new IllegalArgumentException(
						" the attribute[propertyName] of Result element can not be empty");
			}
			if (StringUtils.isEmpty(selector.trim())) {
				throw new IllegalArgumentException(
						" the attribute[selector] of Result element can not be empty");
			}
			if (StringUtils.isEmpty(absolute.trim())) {
				absolute = "false";
			}

			String selectorParts[] = selector.split("\\|");

			if (selectorParts.length == 1
					&& !selectorParts[0].equalsIgnoreCase("html")
					&& StringUtils.isEmpty(handler)) {
				throw new IllegalArgumentException(
						"if you did not declear the where to parse. you must declear handler. for example:\r\n"
								+ "<result selector=\"p a\" class=\"string\" handler=\"com.abc.DefaultHandler\" propertyName=\"name\"/>");
			}

			r.setClazz(Utils.getFullClassName(clazz1.trim()));
			r.setPropertyName(propertyName.trim());
			if (handler != null) {
				r.setHandler(handler.trim());
			}
			r.setSelector(selector);
			if (absolute != null && absolute.trim().equalsIgnoreCase("true")) {
				r.setAbsolute(true);
			}

			resultConfig.addResult(r);
		}

		return resultConfig;
	}

	/**
	 * 解析Parameter标签
	 * 
	 * @param ele
	 * @return
	 */
	private ParameterConfig parseParameterDefinition(Element ele) {
		ParameterConfig parameterConfig = null;

		Elements eles = ele.select("parameters");
		if (eles != null && eles.size() == 1) {
			Element parametersEle = eles.first();
			String clazz = parametersEle.attr("class");
			if (StringUtils.isEmpty(clazz.trim())) {
				throw new IllegalArgumentException(
						"the attribute[class] of Parameters element can not be empty");
			}

			parameterConfig = new ParameterConfig();

			parameterConfig.setClassFullName(Utils.getFullClassName(clazz));

			Elements parameterEles = parametersEle.select("parameter");
			if (parameterEles == null || parameterEles.size() == 0) {
				throw new IllegalArgumentException(
						" There is no Parameters element exist.");
			}
			for (Element e : parameterEles) {
				Parameter p = new Parameter();
				String parameterName = e.attr("parameterName");
				String propertyName = e.attr("propertyName");

				if (StringUtils.isEmpty(parameterName.trim())) {
					throw new IllegalArgumentException(
							" the attribute[parameterName] of Parameter element can not be empty");
				}
				if (StringUtils.isEmpty(propertyName.trim())) {
					throw new IllegalArgumentException(
							" the attribute[propertyName] of Parameter element can not be empty");
				}
				p.setParameterName(parameterName.trim());
				p.setPropertyName(propertyName.trim());

				parameterConfig.addParameter(p);
			}
		}

		return parameterConfig;
	}

	public ParseConfig getParseConfig(String id) {
		if (id == null || id.trim().equals("")) {
			return null;
		}
		return this.parseConfigMap.get(id);
	}

	public HeaderConfig getHeaderConfig(String id) {
		if (id == null || id.trim().equals("")) {
			return null;
		}
		return this.headerConfigMap.get(id);
	}

}
